#pragma once
#include <zCoreConfig.hpp>
#include <Define.hpp>
#include <common.hpp>

namespace zzz {
typedef pair<int, zint64> TableItem;

class ZCORE_CLASS RecordItem {
public:
  RecordItem(FILE *fp, zint64 itebegin_pos_, RecordItem *parent);

  void ReadTable();
  RecordItem* ReadChild(const zint32 label);
  RecordItem* ReadFinish();

  RecordItem* ReadRepeatBegin(const zint32 label);
  RecordItem* ReadRepeatChild();
  size_t GetRepeatNumber();
  RecordItem* ReadRepeatEnd();

  size_t Read(const zint32 label, void *data, size_t size, size_t count);
  size_t Read(void *data, size_t size, size_t count);

  ////////////////////////////////////////////
  RecordItem* WriteChild(const zint32 label);
  RecordItem* WriteFinish();

  RecordItem* WriteRepeatBegin(const zint32 label);
  RecordItem* WriteRepeatChild();
  RecordItem* WriteRepeatEnd();

  size_t Write(const zint32 label, const void *data, size_t size, size_t count);
  size_t Write(const void *data, size_t size, size_t count);

  bool LabelExist(const zint32 label);
  size_t GetItemNumber();
private:
  FILE *fp_;
  zint64 itebegin_pos__;
  zint64 table_begin_pos_;
  vector<TableItem> table_;
  RecordItem *child_;
  RecordItem *parent_;
  int repeat_label_;
  bool CheckLabelExist(const zint32 label);
  zint64 GetChildPos(const zint32 label);
  zint64 GetPos();
  void SetPos(zint64 pos);
  friend class RecordFile;
};

class ZCORE_CLASS RecordFile {
public:
  RecordFile();
  /// Start loading file, it will open the file and wait to read.
  void LoadFileBegin(const string &filename);

  /// Finish loading file, it will close file.
  void LoadFileEnd();

  /// Start saving file, it will open the file and wait to write.
  void SaveFileBegin(const string &filename);

  /// Finishi saving file, it will write additional data to finish the RecordFile structure
  void SaveFileEnd();

  ///////////////////////////////////////////////////
  /// Read function
  /// Start reading a new child node
  void ReadChildBegin(const zint32 label);
  /// Finish reading the current node and return to parent node
  void ReadChildEnd();

  /// Start reading a repeat child node
  void ReadRepeatBegin(const zint32 label);
  /// Start reading the next repeat child, this need to be called even
  /// before reading the first repeat child. When it return false, means
  /// there is no more child.
  bool ReadRepeatChild();
  /// Finish reading a repeat child node.
  void ReadRepeatEnd();

  size_t Read(void *data, size_t size, size_t count);
  size_t Read(const zint32 label, void *data, size_t size, size_t count);
  
  //////////////////////////////////////////////////
  /// Write funciton
  /// Start writing a new child node.
  void WriteChildBegin(const zint32 label);
  /// Finish writing the current node and return to parent node.
  void WriteChildEnd();

  /// Start writing a repeat child node
  void WriteRepeatBegin(const zint32 label);
  /// Start writing the next repeat child, this need to be called even
  /// before writing the first repeat child.
  void WriteRepeatChild();
  /// Finish writing a repeat child node.
  void WriteRepeatEnd();

  size_t Write(const void *data, size_t size, size_t count);
  size_t Write(const zint32 label, const void *data, size_t size, size_t count);
  
  bool LabelExist(const zint32 label);
private:
  bool write_;
  RecordItem *head_;
  RecordItem *cur_;
  FILE* fp_;
};
};  // namespace zzz